package com.ruoyi.game.core.card;

import com.ruoyi.game.core.GameSetPos;
import com.ruoyi.game.core.GameRoundCore;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 牌型判断/出牌策略接口
 */
@Slf4j
public abstract class BaseCardType {

    /**
     * 出牌类型标识（可选）
     */
    public abstract String getName();

    /**
     * 首出场景下 AI 执行的出牌逻辑
     */
    public abstract List<Integer> firstAutoOut(GameSetPos pos);

    /**
     * 跟牌场景下 AI 执行的出牌逻辑
     */
    public abstract List<Integer> followAutoOut(GameSetPos pos, GameRoundCore round);


    /**
     * 尝试从 colors 中找连对牌，填入 curCards
     */
    public void followLianDuiSanPais(List<Integer> curCards, List<Integer> colors, int firstOutCardSize, int zhuColor, int zhuValue, int targetColor) {
        if (curCards.size() >= firstOutCardSize) return;

        List<List<Integer>> lianDuis = lianDuiList(colors, zhuColor, zhuValue, targetColor);

        // 建议排序在这里做
        lianDuis.sort((o1, o2) -> {
            if (o1.size() != o2.size()) return Integer.compare(o2.size(), o1.size()); // 长度降序
            int v1 = BasePoker.getCardValueEx(o1.get(0));
            int v2 = BasePoker.getCardValueEx(o2.get(0));
            return Integer.compare(v1, v2);
        });

        if (!lianDuis.isEmpty()) {
            List<Integer> lianDui = lianDuis.get(0);
            if (firstOutCardSize < curCards.size() + lianDui.size()) {
                lianDui = lianDui.subList(0, firstOutCardSize - curCards.size());
            }
            curCards.addAll(lianDui);
            if (!BasePokerLogic.removeExactCards(colors, lianDui)) {
                log.warn("[followLianDui] 精确移除出错：colors 中不包含完整的连对 {}", BasePoker.toCardString(lianDui));
            }

        }
    }

    /**
     * 尝试从 colors 中找对子牌，填入 curCards
     */
    public void followDuiSanPais(List<Integer> curCards, List<Integer> colors, int firstOutCardSize, int zhuColor, int zhuValue, int targetColor) {
        if (curCards.size() >= firstOutCardSize) return;

        List<List<Integer>> duiList = duiList(curCards, zhuColor, zhuValue, targetColor);
        for (List<Integer> dui : duiList) {
            if (firstOutCardSize < curCards.size() + dui.size()) return;

            curCards.addAll(dui);
            BasePokerLogic.removeExactCards(colors, dui);
            if (curCards.size() >= firstOutCardSize) break;
        }
    }

    /**
     * 补散牌：在主/副牌中补足指定数量的牌，支持目标花色限制
     *
     * @param curCards     当前已选的牌
     * @param privateCards 玩家手牌
     * @param needCount    总共需要的牌张数
     * @param zhuColor     当前主花色
     * @param zhuValue     当前主点值
     * @param targetColor  目标花色，-1 表示不限制
     */
    public void followSanPais(List<Integer> curCards, List<Integer> privateCards, int needCount,
                              int zhuColor, int zhuValue, int targetColor) {
        log.info("[SanPai] 开始补散牌：当前已有={}张，目标={}张，主花色={}，主点值={}，目标花色={}",
                curCards.size(), needCount, zhuColor, zhuValue, targetColor);

        if (curCards.size() >= needCount) {
            log.info("[SanPai] 当前牌数已满足，无需补牌");
            return;
        }

        List<Integer> mainCards = new ArrayList<>();
        List<Integer> viceCards = new ArrayList<>();

        for (int card : privateCards) {
            if (BasePoker.isZhuPai(card, zhuColor, zhuValue)) {
                mainCards.add(card);
            } else {
                viceCards.add(card);
            }
        }

        log.info("[SanPai] 主牌数={}，副牌数={}", mainCards.size(), viceCards.size());

        // 副牌区：先找目标花色
        if (targetColor != -1) {
            for (int i = viceCards.size() - 1; i >= 0; i--) {
                int card = viceCards.get(i);
                if (BasePoker.getCardColor(card) == targetColor && !curCards.contains(card)) {
                    curCards.add(card);
                    log.info("[SanPai] 补目标花色副牌：{}", BasePoker.getCardDescription(card));
                }
                if (curCards.size() == needCount) {
                    log.info("[SanPai] 补牌完成，已达目标");
                    return;
                }
            }
        }

        // 副牌区：随便补
        for (int i = viceCards.size() - 1; i >= 0; i--) {
            int card = viceCards.get(i);
            if (!curCards.contains(card)) {
                curCards.add(card);
                log.info("[SanPai] 补副牌（任意）：{}", BasePoker.getCardDescription(card));
            }
            if (curCards.size() == needCount) {
                log.info("[SanPai] 补牌完成，已达目标");
                return;
            }
        }

        // 主牌区：甭管花色，直接补
        for (int i = mainCards.size() - 1; i >= 0; i--) {
            int card = mainCards.get(i);
            if (!curCards.contains(card)) {
                curCards.add(card);
                log.info("[SanPai] 补主牌：{}", BasePoker.getCardDescription(card));
            }
            if (curCards.size() == needCount) {
                log.info("[SanPai] 补牌完成，已达目标");
                return;
            }
        }
        log.warn("[SanPai] 补牌结束，但仍未达到目标，当前={}张", curCards.size());
    }

    /**
     * 提取对子组合（主副牌分离，支持指定花色）
     *
     * @param cards 所有手牌
     * @param zhuColor 主花色
     * @param zhuValue 主点数
     * @param targetColor 指定花色（-1 表示不限，zhuColor 表示主牌）
     * @return 满足条件的对子组合，每组为 List<Integer>
     */
    public static List<List<Integer>> duiList(List<Integer> cards, int zhuColor, int zhuValue, int targetColor) {
        log.info("[对子分析] 开始处理，共 {} 张牌，主花色={}，主点数={}，目标花色={}",
                cards.size(), zhuColor, zhuValue, targetColor);

        List<List<Integer>> result = new ArrayList<>();
        List<Integer> mainCards = new ArrayList<>();
        Map<Integer, List<Integer>> subColorMap = new HashMap<>();

        for (Integer card : cards) {
            int color = BasePoker.getCardColor(card);
            boolean isMain = BasePoker.isZhuPai(card, zhuColor, zhuValue);
            if (isMain) {
                mainCards.add(card);
            } else {
                subColorMap.computeIfAbsent(color, k -> new ArrayList<>()).add(card);
            }
        }

        log.info("[对子分析] 主牌堆：{}", BasePoker.toCardString(mainCards));
        for (Map.Entry<Integer, List<Integer>> entry : subColorMap.entrySet()) {
            log.info("[对子分析] 副牌堆-花色{}：{}", entry.getKey(), BasePoker.toCardString(entry.getValue()));
        }

        if (targetColor == zhuColor) {
            log.info("[对子分析] 查找主牌中的对子");
            result.addAll(extractDuiFromList(mainCards, zhuColor, zhuValue));
        } else if (targetColor != -1) {
            List<Integer> colorCards = subColorMap.getOrDefault(targetColor, Collections.emptyList());
            log.info("[对子分析] 查找花色={} 副牌中的对子：{}", targetColor, BasePoker.toCardString(colorCards));
            result.addAll(extractDuiFromList(colorCards, zhuColor, zhuValue));
        } else {
            log.info("[对子分析] 查找所有花色中的对子");
            result.addAll(extractDuiFromList(mainCards, zhuColor, zhuValue));
            for (List<Integer> subColorCards : subColorMap.values()) {
                result.addAll(extractDuiFromList(subColorCards, zhuColor, zhuValue));
            }
        }

        log.info("[对子分析] 共找到 {} 个对子组合", result.size());
        for (List<Integer> dui : result) {
            log.info("[对子分析] 对子：{}", BasePoker.toCardString(dui));
        }

        return result;
    }

    /**
     * 提取某个牌堆中的对子（每个牌值出现两次）
     */
    private static List<List<Integer>> extractDuiFromList(List<Integer> cards, int zhuColor, int zhuValue) {
        Map<Integer, List<Integer>> grouped = new HashMap<>();
        for (Integer card : cards) {
            int baseVal = BasePoker.getCardValue(card);
            int mappedVal;
            if (BasePoker.isJoker(card)) {
                mappedVal = card == 0x41 ? 17 : 18;
            } else if (BasePoker.isZhuPai(card, zhuColor, zhuValue) && baseVal == zhuValue) {
                mappedVal = 16;
            } else {
                mappedVal = baseVal;
            }
            grouped.computeIfAbsent(mappedVal, k -> new ArrayList<>()).add(card);
        }

        List<List<Integer>> result = new ArrayList<>();
        for (Map.Entry<Integer, List<Integer>> entry : grouped.entrySet()) {
            List<Integer> group = entry.getValue();

            // 按花色分组
            Map<Integer, List<Integer>> colorMap = group.stream()
                    .collect(Collectors.groupingBy(BasePoker::getCardColor));

            for (Map.Entry<Integer, List<Integer>> colorEntry : colorMap.entrySet()) {
                List<Integer> sameColorCards = colorEntry.getValue();
                if (sameColorCards.size() >= 2) {
                    // 只取前两张
                    result.add(sameColorCards.subList(0, 2));
                    break; // 每个 mappedVal 只取一个合法对子
                }
            }
        }
        return result;
    }


    /**
     * 提取连对组合（主副牌分离，支持指定花色）
     *
     * @param cards 所有手牌
     * @param zhuColor 主花色
     * @param zhuValue 主点数（如 2）
     * @param targetColor 指定花色（-1 表示不限，zhuColor 表示主牌）
     * @return 满足条件的连对组合，每组为 List<Integer>
     */
    public static List<List<Integer>> lianDuiList(List<Integer> cards, int zhuColor, int zhuValue, int targetColor) {
        log.info("[连对分析] 开始处理，共 {} 张牌，主花色={}，主点数={}，目标花色={}",
                cards.size(), zhuColor, zhuValue, targetColor);

        List<List<Integer>> result = new ArrayList<>();

        // 分主副堆（副牌再按花色分）
        List<Integer> mainCards = new ArrayList<>();
        Map<Integer, List<Integer>> subColorMap = new HashMap<>(); // 副牌按花色分类


        for (Integer card : cards) {
            int color = BasePoker.getCardColor(card);
            int baseVal = BasePoker.getCardValue(card);

            boolean isMain = BasePoker.isZhuPai(card, zhuColor, zhuValue);
            // 加入主或副堆（副牌按花色）
            if (isMain) {
                mainCards.add(card);
            } else {
                subColorMap.computeIfAbsent(color, k -> new ArrayList<>()).add(card);
            }
        }

        // ✅ 处理目标花色
        log.info("[连对分析] 主牌堆：{}", BasePoker.toCardString(mainCards));
        for (Map.Entry<Integer, List<Integer>> entry : subColorMap.entrySet()) {
            log.info("[连对分析] 副牌堆-花色{}：{}", entry.getKey(), BasePoker.toCardString(entry.getValue()));
        }

        if (targetColor == zhuColor) {
            log.info("[连对分析] 查找主牌中的连对");
            result.addAll(extractLianDuiFromCardList(mainCards, zhuColor, zhuValue));
        } else if (targetColor != -1) {
            List<Integer> colorCards = subColorMap.getOrDefault(targetColor, Collections.emptyList());
            log.info("[连对分析] 查找花色={} 副牌中的连对：{}", targetColor, BasePoker.toCardString(colorCards));
            result.addAll(extractLianDuiFromCardList(colorCards, zhuColor, zhuValue));
        } else {
            // 所有主副花色中找连对
            log.info("[连对分析] 查找所有花色中可能的连对（主 + 副）");
            result.addAll(extractLianDuiFromCardList(mainCards, zhuColor, zhuValue));
            for (List<Integer> subColorCards : subColorMap.values()) {
                result.addAll(extractLianDuiFromCardList(subColorCards, zhuColor, zhuValue));
            }
        }

        log.info("[连对分析] 最终找到 {} 组连对", result.size());
        for (List<Integer> group : result) {
            log.info("[连对分析] 连对组：{}", BasePoker.toCardString(group));
        }
        return result;
    }

    /**
     * 提取同一牌堆中的连对组（不区分主副，仅处理传入牌）
     * 要求：按牌值排序，相同值>=2即可组成连对
     */
    private static List<List<Integer>> extractLianDuiFromCardList(List<Integer> cards, int zhuColor, int zhuValue) {
        // Step 1: 分组 - 先按牌力（含主副2映射）分组
        Map<Integer, List<Integer>> powerGrouped = new HashMap<>();
        for (Integer card : cards) {
            int power = BasePoker.getCardPower(card, zhuColor, zhuValue);
            powerGrouped.computeIfAbsent(power, k -> new ArrayList<>()).add(card);
        }

        log.info("[连对提取] 分组完成：{}", BasePoker.toCardGroupLog(powerGrouped));

        List<List<Integer>> result = new ArrayList<>();
        List<Integer> current = new ArrayList<>();

        // Step 2: 遍历牌力值，从 2 ~ 18 依次构建连对链
        for (int power = 2; power <= 18; power++) {
            List<Integer> group = powerGrouped.get(power);
            if (group == null || group.size() < 2) {
                // 没有该点数，断链
                if (current.size() >= 4) {
                    result.add(new ArrayList<>(current));
                    log.info("[连对提取] 添加连对组合：{}", BasePoker.toCardString(current));
                }
                current.clear();
                continue;
            }

            // Step 3: 查找是否存在一对完全相同的牌（花色 + 点数）
            boolean foundPair = false;
            for (int i = 0; i < group.size(); i++) {
                for (int j = i + 1; j < group.size(); j++) {
                    if (group.get(i).equals(group.get(j))) {
                        current.add(group.get(i));
                        current.add(group.get(j));
                        foundPair = true;
                        break;
                    }
                }
                if (foundPair) break;
            }

            if (!foundPair) {
                if (current.size() >= 4) {
                    result.add(new ArrayList<>(current));
                    log.info("[连对提取] 添加连对组合：{}", BasePoker.toCardString(current));
                }
                current.clear();
            }
        }

        // 收尾
        if (current.size() >= 4) {
            result.add(current);
            log.info("[连对提取] 添加尾部连对组合：{}", BasePoker.toCardString(current));
        }

        return result;
    }

    /**
     * 从一个已分好主副的牌堆中提取同花色的连对
     */
    private List<List<Integer>> extractLianDuiByColor(Map<Integer, List<Integer>> grouped, int targetColor) {
        List<List<Integer>> result = new ArrayList<>();

        // Step 1: 构造 value+color 的组合 key
        Map<Integer, List<Integer>> colorGrouped = new TreeMap<>();
        for (Map.Entry<Integer, List<Integer>> entry : grouped.entrySet()) {
            int value = entry.getKey();
            List<Integer> group = entry.getValue();

            Map<Integer, List<Integer>> byColor = group.stream()
                    .filter(c -> targetColor == -1 || BasePoker.getCardColor(c) == targetColor)
                    .collect(Collectors.groupingBy(BasePoker::getCardColor));

            for (Map.Entry<Integer, List<Integer>> colorEntry : byColor.entrySet()) {
                int color = colorEntry.getKey();
                List<Integer> cards = colorEntry.getValue();

                if (cards.size() >= 2) {
                    int key = value * 10 + color;
                    colorGrouped.put(key, cards);
                }
            }
        }

        // Step 2: 提取连续 value 且同花色的连对
        List<Integer> sortedKeys = new ArrayList<>(colorGrouped.keySet());
        Collections.sort(sortedKeys);

        int i = 0;
        while (i < sortedKeys.size()) {
            List<Integer> buffer = new ArrayList<>();
            int key1 = sortedKeys.get(i);
            int value1 = key1 / 10;
            int color1 = key1 % 10;

            buffer.addAll(colorGrouped.get(key1).subList(0, 2));
            int count = 1;

            for (int j = i + 1; j < sortedKeys.size(); j++) {
                int key2 = sortedKeys.get(j);
                int value2 = key2 / 10;
                int color2 = key2 % 10;

                if (color2 != color1 || value2 != value1 + count) break;

                buffer.addAll(colorGrouped.get(key2).subList(0, 2));
                count++;
            }

            if (buffer.size() >= 4) {
                result.add(new ArrayList<>(buffer));
            }

            i++;
        }

        return result;
    }
    protected List<Integer> getFollowableCards(GameSetPos pos, GameRoundCore round) {
        if (round.getRoundCards().isEmpty()) {
            log.warn("[FollowCard] 当前回合无首出牌，无法判断可跟牌");
            return Collections.emptyList();
        }

        int firstCard = round.getFirstOutCards().get(0);
        int zhuColor = round.getRoomSet().getLiangZhuInfo().getZhuColor();
        int zhuValue = round.getRoomSet().getZhuValue();

        boolean isFirstZhu = BasePoker.isZhuPai(firstCard, zhuColor, zhuValue);
        List<Integer> hand = pos.getHandCards();

        log.info("[FollowCard] 首出牌={}（{}），主花色={} 主点数={}，首出为{}牌",
                BasePoker.getCardDescription(firstCard), firstCard, zhuColor, zhuValue, isFirstZhu ? "主" : "副");

        List<Integer> followable = hand.stream()
                .filter(c -> {
                    if (isFirstZhu) {
                        return BasePoker.isZhuPai(c, zhuColor, zhuValue);
                    } else {
                        return BasePoker.getCardColor(c) == BasePoker.getCardColor(firstCard)
                                && !BasePoker.isZhuPai(c, zhuColor, zhuValue);
                    }
                })
                .collect(Collectors.toList());

        log.info("[FollowCard] seat={} 可跟牌列表：{}", pos.getSeatIndex(), BasePoker.toCardString(followable));

        return followable;
    }

}
